package com.android.gl2jni;

import static android.content.Context.WINDOW_SERVICE;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.SurfaceTexture;
import android.graphics.SurfaceTexture.OnFrameAvailableListener;
import android.hardware.Camera;
import android.hardware.Camera.Parameters;
import android.hardware.Camera.Size;
import android.media.AudioManager;
import android.media.MediaPlayer;
import android.media.MediaPlayer.OnPreparedListener;
import android.media.MediaPlayer.OnVideoSizeChangedListener;
import android.net.Uri;
import android.opengl.GLES11Ext;
import android.opengl.GLES20;
import android.opengl.GLSurfaceView;
import android.opengl.GLUtils;
import android.opengl.Matrix;
import android.os.Environment;
import android.os.SystemClock;
import android.util.AttributeSet;
import android.util.Log;
import android.view.Display;
import android.view.Surface;
import android.view.WindowManager;
import java.io.File;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;
import java.nio.ShortBuffer;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.GL10;

/**
 * Create time: 2018/9/4.
 */
public class MyGLView extends GLSurfaceView {
  static final String TAG = "MyGLView";
  //MyRenderer renderer;
  CameraRenderer renderer;
  //VideoRenderer renderer;

  public MyGLView(Context context) {
    super(context);
    init();
  }

  public MyGLView(Context context, AttributeSet attrs) {
    super(context, attrs);
    init();
  }

  private void init() {
    setEGLContextClientVersion(2);
    //setEGLConfigChooser(8, 8, 8, 8, 16, 0);
    //renderer = new MyRenderer();
    renderer = new CameraRenderer();
    //renderer = new VideoRenderer();
    setRenderer(renderer);
    //setRenderMode(RENDERMODE_CONTINUOUSLY);
    setRenderMode(RENDERMODE_WHEN_DIRTY);
  }

  public void setEye(float eyeX, float eyeY, float eyeZ) {
    //renderer.setLookAtM(eyeX, eyeY, eyeZ);
  }

  public void rotateCamera() {
    //renderer.rotateCamera();
  }

  public void updateTexVertex(int rotation, boolean flipV, boolean flipH) {
    renderer.updateTexVertex(rotation, flipV, flipH);
  }

  public void destroy() {
    renderer.destroy();
  }

  private class VideoRenderer implements Renderer, OnFrameAvailableListener {
    MediaPlayer mediaPlayer;
    SurfaceTexture mSurfaceTexture;
    private final float[] mProjectionMatrix = new float[16];
    private final float[] mProjectionMatrix2 = new float[16];
    private final float[] mSTMatrix = new float[16];
    TextureSquare texSquare;
    int videoWidth, videoHeight;
    int surfaceWidth, surfaceHeight;
    int rotation;
    boolean needUpdate;
    private FBO fbo;
    Square square;

    public VideoRenderer() {
      Matrix.setIdentityM(mProjectionMatrix, 0);
      Matrix.setIdentityM(mProjectionMatrix2, 0);
      Matrix.setIdentityM(mSTMatrix, 0);
    }

    private void initMediaplayer() {
      Surface surface = new Surface(mSurfaceTexture);
      mediaPlayer = new MediaPlayer();
      try{
        mediaPlayer.setDataSource(getContext(),
            Uri.fromFile(new File(Environment.getExternalStorageDirectory(), "test2.mp4")));
      }catch (IOException e){
        e.printStackTrace();
      }
      mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
      mediaPlayer.setLooping(true);
      mediaPlayer.setSurface(surface);
      mediaPlayer.setOnVideoSizeChangedListener(new OnVideoSizeChangedListener() {
        @Override
        public void onVideoSizeChanged(MediaPlayer mp, int width, int height) {
          Log.d(TAG, "onVideoSizeChanged: " + width + "x" + height);
          videoWidth = width;
          videoHeight = height;
          needUpdate = true;
        }
      });
      mediaPlayer.setOnPreparedListener(new OnPreparedListener() {
        @Override
        public void onPrepared(MediaPlayer mp) {
          mediaPlayer.start();
        }
      });
      mediaPlayer.prepareAsync();
      surface.release();
    }

    @Override
    public void onFrameAvailable(SurfaceTexture surfaceTexture) {
      requestRender();
    }

    @Override
    public void onSurfaceCreated(GL10 gl, EGLConfig config) {
      texSquare = new TextureSquare();
      square = new Square(getContext());
      mSurfaceTexture = new SurfaceTexture(texSquare.mOESTexName);
      mSurfaceTexture.setOnFrameAvailableListener(this);
      initMediaplayer();
    }

    @Override
    public void onSurfaceChanged(GL10 gl, int width, int height) {
      GLES20.glViewport(0, 0, width, height);
      surfaceWidth = width;
      surfaceHeight = height;
      Log.d(TAG, "onSurfaceChanged: " + width + "x" + height);
      needUpdate = true;
      if (fbo != null) {
        fbo.destroy();
      }
      fbo = FBO.create(width, height);
      Matrix.orthoM(mProjectionMatrix2, 0, -1f, 1f, -1f, 1f,
          -0.1f, 10f);
    }

    @Override
    public void onDrawFrame(GL10 gl) {
      if (mSurfaceTexture == null) {
        return;
      }
      mSurfaceTexture.updateTexImage();
      mSurfaceTexture.getTransformMatrix(mSTMatrix);
      GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, fbo.getFrameBuffer());
      GLES20.glClearColor(1f, 0f, 0f, 1.0f);
      GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);
      GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
      GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, texSquare.mOESTexName);
      if (needUpdate) {
        updateProjection();
        needUpdate = false;
      }
      texSquare.draw(mProjectionMatrix, mSTMatrix);
      GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, 0);
      int textureId = fbo.getFrameBufferTextureId();
      GLES20.glClearColor(0.0f, 1.0f, 0.0f, 1.0f);
      GLES20.glClear(GLES20.GL_DEPTH_BUFFER_BIT | GLES20.GL_COLOR_BUFFER_BIT);
      GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
      GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textureId);
      square.draw(mProjectionMatrix2, surfaceWidth, surfaceHeight);
    }

    private void updateProjection() {
      float videoRatio = rotation == 90 || rotation == 270 ?
          (float)videoHeight / videoWidth : (float)videoWidth / videoHeight;
      float surfaceRatio = (float)surfaceWidth / surfaceHeight;
      if (videoRatio > surfaceRatio) {
        float scale = videoRatio / surfaceRatio;
        Matrix.orthoM(mProjectionMatrix, 0, -1f, 1f, -scale, scale,
            -0.1f, 10f);
      } else {
        float scale = surfaceRatio / videoRatio;
        Matrix.orthoM(mProjectionMatrix, 0, -scale, scale, -1f, 1f,
            -0.1f, 10f);
      }
    }

    void updateTexVertex(int rotation, boolean flipV, boolean flipH) {
      this.rotation = rotation;
      texSquare.updateTexVertex(rotation, flipV, flipH);
      needUpdate = true;
    }

    void destroy() {
      texSquare.destroy();
      mediaPlayer.stop();
      mediaPlayer.release();
      square.destroy();
    }
  }

  private class CameraRenderer implements Renderer, OnFrameAvailableListener {
    private final float[] mProjectionMatrix = new float[16];
    private final float[] mProjectionMatrix2 = new float[16];
    private final float[] mSTMatrix = new float[16];
    int surfaceWidth, surfaceHeight;
    int rotation;
    boolean needUpdate;
    SurfaceTexture mSurfaceTexture;
    Camera camera;
    Camera.Size cameraSize;
    private FBO fbo;
    private TextureSquare textureSquare;
    private Square square;

    public CameraRenderer() {
      Matrix.setIdentityM(mProjectionMatrix, 0);
      Matrix.setIdentityM(mProjectionMatrix2, 0);
      Matrix.setIdentityM(mSTMatrix, 0);
    }

    private void openCamera() {
      camera = Camera.open(0);
      if (camera != null) {
        Camera.CameraInfo info = new Camera.CameraInfo();
        Camera.getCameraInfo(0, info);
        Log.d(TAG,"cameraId: " + 0 + ", orientation: " + info.orientation);
        Parameters parameters = camera.getParameters();
        List<Camera.Size> supportedPictureSizesList = parameters.getSupportedPictureSizes();
        List<Camera.Size> supportedVideoSizesList = parameters.getSupportedVideoSizes();
        List<Camera.Size> supportedPreviewSizesList = parameters.getSupportedPreviewSizes();
        Log.i(TAG, "supportedPictureSizesList:");
        logCameraSize(supportedPictureSizesList);
        Log.i(TAG, "supportedVideoSizesList:");
        logCameraSize(supportedVideoSizesList);
        Log.i(TAG, "supportedPreviewSizesList:");
        logCameraSize(supportedPreviewSizesList);
        cameraSize = choosePreferredSize(supportedPictureSizesList, 16f / 9);
        Log.i(TAG, "choosePreferredSize: " + cameraSize.width + " x " + cameraSize.height);
        parameters.setPreviewSize(cameraSize.width, cameraSize.height);
        parameters.setPictureSize(cameraSize.width, cameraSize.height);
        camera.setParameters(parameters);
//        Display display = ((WindowManager)getContext().getSystemService(WINDOW_SERVICE)).getDefaultDisplay();
//
//        if(display.getRotation() == Surface.ROTATION_0) {
//          camera.setDisplayOrientation(90);
//        } else if(display.getRotation() == Surface.ROTATION_270) {
//          camera.setDisplayOrientation(180);
//        }
        needUpdate = true;
        try {
          camera.setPreviewTexture(mSurfaceTexture);
          camera.startPreview();
        } catch (IOException e) {
          e.printStackTrace();
        }
      }
    }

    private void stopCamera() {
      if (camera != null) {
        camera.stopPreview();
        camera.release();
      }
    }

    @Override
    public void onFrameAvailable(SurfaceTexture surfaceTexture) {
      requestRender();
    }

    @Override
    public void onSurfaceCreated(GL10 gl, EGLConfig config) {
      textureSquare = new TextureSquare();
      mSurfaceTexture = new SurfaceTexture(textureSquare.mOESTexName);
      mSurfaceTexture.setOnFrameAvailableListener(this);
      square = new Square(getContext());
      openCamera();
    }

    @Override
    public void onSurfaceChanged(GL10 gl, int width, int height) {
      Log.d(TAG, "onSurfaceChanged: " + width + "x" + height);
      surfaceWidth = width;
      surfaceHeight = height;
      GLES20.glViewport(0, 0, width, height);
      if (fbo != null) {
        fbo.destroy();
      }
      fbo = FBO.create(width, height);
      needUpdate = true;
      Matrix.orthoM(mProjectionMatrix2, 0, -1f, 1f, -1f, 1f, -0.1f, 10f);
    }

    @Override
    public void onDrawFrame(GL10 gl) {
      if (mSurfaceTexture == null) {
        return;
      }
      mSurfaceTexture.updateTexImage();
      mSurfaceTexture.getTransformMatrix(mSTMatrix);

      GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, fbo.getFrameBuffer());

      GLES20.glClearColor(1f, 0f, 0f, 1.0f);
      GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);
      GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
      GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, textureSquare.mOESTexName);
      if (needUpdate) {
        updateProjection();
        needUpdate = false;
      }
      textureSquare.draw(mProjectionMatrix, mSTMatrix);

      GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, 0);
      int textureId = fbo.getFrameBufferTextureId();
      GLES20.glClearColor(0.0f, 1.0f, 0.0f, 1.0f);
      GLES20.glClear(GLES20.GL_DEPTH_BUFFER_BIT | GLES20.GL_COLOR_BUFFER_BIT);
      //激活GL_TEXTURE0，该纹理单元默认激活
      GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
      //绑定纹理数据到GL_TEXTURE0纹理单元上，并且指定其为GL_TEXTURE_2D目标
      GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textureId);
      square.draw(mProjectionMatrix2, surfaceWidth, surfaceHeight);
    }

    private void updateProjection() {
      float videoRatio = rotation == 90 || rotation == 270 ?
          (float)cameraSize.height / cameraSize.width : (float)cameraSize.width / cameraSize.height;
      float surfaceRatio = (float)surfaceWidth / surfaceHeight;
      if (videoRatio > surfaceRatio) {
        float scale = videoRatio / surfaceRatio;
        Matrix.orthoM(mProjectionMatrix, 0, -1f, 1f, -scale, scale,
            -0.1f, 10f);
      } else {
        float scale = surfaceRatio / videoRatio;
        Matrix.orthoM(mProjectionMatrix, 0, -scale, scale, -1f, 1f,
            -0.1f, 10f);
      }
    }

    void updateTexVertex(int rotation, boolean flipV, boolean flipH) {
      if (textureSquare == null) {
        return;
      }
      this.rotation = rotation;
      textureSquare.updateTexVertex(rotation, flipV, flipH);
      needUpdate = true;
    }

    void destroy() {
      stopCamera();
      square.destroy();
      textureSquare.destroy();
    }
  }

  private class MyRenderer implements Renderer {
    private final float[] mMVPMatrix = new float[16];
    private final float[] mProjectionMatrix = new float[16];
    private final float[] mViewMatrix = new float[16];
    private final float[] mRotationMatrix = new float[16];
    private final float[] mScratchMatrix = new float[16];
    private float[] lookAt = new float[3];
    private Triangle triangle;
    private Square square;
    private boolean rotate;

    void destroy() {
      triangle.destroy();
      square.destroy();
    }
    public void rotateCamera() {
      rotate = !rotate;
    }

    void setLookAtM(float x, float y, float z) {
      lookAt[0] = x;
      lookAt[1] = y;
      lookAt[2] = z;
      Matrix.setLookAtM(mViewMatrix, 0, x, y, z,
          0f, 0f, 0f, 0f, 1.0f, 0.0f);
    }

    void setRotateM() {
      long time = SystemClock.uptimeMillis() % 40000L;
      float angle = 0.009f * ((int) time);
      Matrix.setRotateM(mRotationMatrix, 0, angle, 0f, 0f, 1f);
      if (rotate) {
        float x = (float) (Math.sin(angle) * lookAt[2]);
        float z = (float) (Math.cos(angle) * lookAt[2]);
        Matrix.setLookAtM(mViewMatrix, 0, x, lookAt[1], z,
            0f, 0f, 0f, 0f, 1.0f, 0.0f);
      }
    }

    @Override
    public void onSurfaceCreated(GL10 gl, EGLConfig config) {
      GLES20.glClearColor(0.1f, 0.1f, 0.1f, 1.0f);
      triangle = new Triangle();
      square = new Square(getContext());
    }

    @Override
    public void onSurfaceChanged(GL10 gl, int width, int height) {
      GLES20.glViewport(0, 0, width, height);
      float ratio = (float) height / width;
      //Matrix.frustumM(mProjectionMatrix, 0, -1, 1, -ratio, ratio, 0.5f, 10f);
      Matrix.perspectiveM(mProjectionMatrix, 0, 90f, 1 / ratio, 0.1f, 10);
    }

    @Override
    public void onDrawFrame(GL10 gl) {
      GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);
      setRotateM();
      Matrix.multiplyMM(mMVPMatrix, 0, mProjectionMatrix, 0, mViewMatrix, 0);
      Matrix.multiplyMM(mScratchMatrix, 0, mMVPMatrix, 0, mRotationMatrix, 0);
      triangle.draw(mScratchMatrix);
      square.draw(mMVPMatrix, 0, 0);
    }
  }

  private static class Triangle {
    private int mProgram;
    private FloatBuffer vertexBuffer;
    static final int COORDS_PER_VERTEX = 3;
    static float coords[] = {
        0.0f, 1f, 0.0f,
        -1f, -1f, 0.0f,
        1f, -1f, 0.0f
    };
    private FloatBuffer colorBuffer;
    static final int COLOR_COORDS_PER_VERTEX = 4;
    static float[] color = {
        1.0f, 0f, 0f, 1.0f,
        0f, 1.0f, 0f, 1.0f,
        0f, 0f, 1.0f, 1.0f
    };
    private final int vertexCount = coords.length / COORDS_PER_VERTEX;
    private int positionHandler;
    private int colorHandler;
    private int matrixHandler;

    static final String vertexShaderCode =
        "uniform mat4 uMVPMatrix;" +
            "attribute vec4 aPosition;" +
            "varying vec4 vColor;" +
            "attribute vec4 aColor;"+
            "void main() {" +
            "  gl_Position = uMVPMatrix * aPosition;" +
            "  vColor = aColor;" +
            "}";

    static final String fragmentShaderCode =
        "precision mediump float;" +
            "varying vec4 vColor;" +
            "void main() {" +
            "  gl_FragColor = vColor;" +
            "}";

    public Triangle() {
      vertexBuffer = toFloatBuffer(coords);
      colorBuffer = toFloatBuffer(color);
      mProgram = GLES20.glCreateProgram();
      int vertexShader = loadShader(GLES20.GL_VERTEX_SHADER, vertexShaderCode);
      int fragmentShader = loadShader(GLES20.GL_FRAGMENT_SHADER, fragmentShaderCode);
      GLES20.glAttachShader(mProgram, vertexShader);
      GLES20.glAttachShader(mProgram, fragmentShader);
      GLES20.glLinkProgram(mProgram);
      positionHandler = GLES20.glGetAttribLocation(mProgram, "aPosition");
      colorHandler = GLES20.glGetAttribLocation(mProgram, "aColor");
      matrixHandler = GLES20.glGetUniformLocation(mProgram, "uMVPMatrix");
    }

    public void draw(float[] matrix) {
      // 将程序添加到OpenGL ES环境
      GLES20.glUseProgram(mProgram);
      // 启用三角形顶点位置的句柄
      GLES20.glEnableVertexAttribArray(positionHandler);
      //准备三角形坐标数据
      //int vertexStride = COORDS_PER_VERTEX * 4; // 4 bytes per vertex
      GLES20.glVertexAttribPointer(positionHandler, COORDS_PER_VERTEX, GLES20.GL_FLOAT, false,
          0, vertexBuffer);
//      // 获取片段着色器的颜色的句柄
//      int colorHandler = GLES20.glGetUniformLocation(mProgram, "vColor");
//      // 设置绘制三角形的颜色
//      GLES20.glUniform4fv(colorHandler, 1, color, 0);
      GLES20.glEnableVertexAttribArray(colorHandler);
      GLES20.glVertexAttribPointer(colorHandler, COLOR_COORDS_PER_VERTEX, GLES20.GL_FLOAT, false,
          0, colorBuffer);
      // 将投影和视图转换传递给着色器
      GLES20.glUniformMatrix4fv(matrixHandler, 1, false, matrix, 0);
      // 绘制三角形
      GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0, vertexCount);
    }

    public void destroy() {
      GLES20.glDeleteProgram(mProgram);
    }
  }

  private static class Square {
    private int mProgram;
    private FloatBuffer vertexBuffer;
    private FloatBuffer texVertexBuffer;
    private ShortBuffer vertexIndexBuffer;
    static final int COORDS_PER_VERTEX = 3;
    private static final float[] VERTEX = {
        -1f, 1f, 0,  // top left
        -1f, -1f, 0, // bottom left
        1f, -1f, 0,  // bottom right
        1f, 1f, 0,   // top right
    };
    private static final short[] VERTEX_INDEX = {
        0, 1, 2, 0, 2, 3
    };
    static final int COORDS_PER_TEX_VERTEX = 2;
    private static final float[] TEX_VERTEX = {
        0f,0f,//0, 1f,  // bottom left
        0f,1f,//0, 0,  // top left
        1f,1f,//1f, 0,  // top right
        1f,0f,//1f, 1f,  // bottom right
    };
    private int matrixHandler;
    private int aPositionHandler;
    private int texCoordHandler;
    private int texSampleHandler;
    private int location;
    private int iResolutionLocation;
    private int mTexName;
    private final long START_TIME;
    private static final String VERTEX_SHADER =
        "uniform mat4 uMVPMatrix;" +
            "attribute vec4 vPosition;" +
            "attribute vec2 a_texCoord;" +
            "varying vec2 vTextureCoord;" +
            "void main() {" +
            "  gl_Position = uMVPMatrix * vPosition;" +
            "  vTextureCoord = a_texCoord;" +
            "}";
    private static final String FRAGMENT_SHADER =
        "//设置float类型默认精度，顶点着色器默认highp，片元着色器需要用户声明\n" +
            "precision mediump float;" +
            "varying vec2 vTextureCoord;" +
            "uniform sampler2D sTexture;" +
            "void main() {" +
            "  vec4 color = texture2D(sTexture, vTextureCoord);" +
            "  float result = 0.299 * color.r + 0.587 * color.g + 0.114 * color.b;" +
            "  gl_FragColor = vec4(result, result, result, 1.0);" +
            //" gl_FragColor = vec4(0.0, 0.0, 1, 1f);" +
            "}";
    public Square(Context context) {
      mProgram = GLES20.glCreateProgram();
      vertexBuffer = toFloatBuffer(VERTEX);
      texVertexBuffer = toFloatBuffer(TEX_VERTEX);
      vertexIndexBuffer = toShortBuffer(VERTEX_INDEX);
      int vertexShader = loadShader(GLES20.GL_VERTEX_SHADER, VERTEX_SHADER);
      //int fragmentShader = loadShader(GLES20.GL_FRAGMENT_SHADER, FRAGMENT_SHADER);
      String fragmentShaderSource = ShaderUtils.readAssetsTextFile(context, "filter/fsh/shadertoy/ascii_art.glsl");
      int fragmentShader = loadShader(GLES20.GL_FRAGMENT_SHADER, fragmentShaderSource);
      GLES20.glAttachShader(mProgram, vertexShader);
      GLES20.glAttachShader(mProgram, fragmentShader);
      GLES20.glLinkProgram(mProgram);
      aPositionHandler = GLES20.glGetAttribLocation(mProgram, "vPosition");
      matrixHandler = GLES20.glGetUniformLocation(mProgram, "uMVPMatrix");
      texCoordHandler = GLES20.glGetAttribLocation(mProgram, "a_texCoord");
      texSampleHandler = GLES20.glGetUniformLocation(mProgram, "sTexture");
      iResolutionLocation = GLES20.glGetUniformLocation(mProgram, "iResolution");
      location = GLES20.glGetUniformLocation(mProgram, "iGlobalTime");
      START_TIME = System.currentTimeMillis();
    }

//    public Square(Context context) {
//      this();
//      mTexName = createTexture(context);
//    }

    private int createTexture(Context context) {
      int[] texNames = new int[1];
      GLES20.glGenTextures(1, texNames, 0);
      //激活GL_TEXTURE0，该纹理单元默认激活
      GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
      //绑定纹理数据到GL_TEXTURE0纹理单元上，并且指定其为GL_TEXTURE_2D目标
      GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, texNames[0]);
      GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_LINEAR);
      GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_LINEAR);
      GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_REPEAT);
      GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_REPEAT);
      Bitmap bitmap = BitmapFactory.decodeResource(context.getResources(), R.drawable.image);
      GLUtils.texImage2D(GLES20.GL_TEXTURE_2D, 0, bitmap, 0);
      bitmap.recycle();
      return texNames[0];
    }

    public void draw(float[] matrix, int surfaceWidth, int surfaceHeight) {
      GLES20.glUseProgram(mProgram);
      GLES20.glUniform3fv(iResolutionLocation, 1,
          FloatBuffer.wrap(new float[]{(float) surfaceWidth, (float) surfaceHeight, 1.0f}));
      float currentTime = ((float) (System.currentTimeMillis() - START_TIME)) / 1000.0f;
      GLES20.glUniform1f(location, currentTime);
      GLES20.glEnableVertexAttribArray(aPositionHandler);
      GLES20.glVertexAttribPointer(aPositionHandler, COORDS_PER_VERTEX,
          GLES20.GL_FLOAT, false, 0, vertexBuffer);
      GLES20.glEnableVertexAttribArray(texCoordHandler);
      GLES20.glVertexAttribPointer(texCoordHandler, COORDS_PER_TEX_VERTEX,
          GLES20.GL_FLOAT, false, 0, texVertexBuffer);
      GLES20.glUniformMatrix4fv(matrixHandler, 1, false, matrix, 0);
      //将纹理对象设置到0号单元上
      GLES20.glUniform1i(texSampleHandler, 0);
      //GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0, 4);
      GLES20.glDrawElements(GLES20.GL_TRIANGLES, VERTEX_INDEX.length, GLES20.GL_UNSIGNED_SHORT, vertexIndexBuffer);
    }

    void destroy() {
      if (mTexName != 0) {
        GLES20.glDeleteTextures(1, new int[] { mTexName }, 0);
      }
      GLES20.glDeleteProgram(mProgram);
    }
  }

  private class TextureSquare {
    private final String vertexShaderCode =
        "uniform mat4 uMVPMatrix;\n" +
            "uniform mat4 uSTMatrix;\n" +
            "attribute vec4 aPosition;\n" +//NDK坐标点
            "attribute vec4 aTexCoord;\n" +
            "varying   vec2 textureCoordinate;\n" +//纹理坐标点变换后输出
            "\n" +
            "void main() {\n" +
            "    gl_Position = uMVPMatrix * aPosition;\n" +
            "    textureCoordinate = (uSTMatrix * aTexCoord).xy;\n" +
            "}\n";

    private final String fragmentShaderCode =
        "#extension GL_OES_EGL_image_external : require\n" +
            "precision mediump float;\n" +
            "uniform samplerExternalOES videoTex;\n" +
            "varying vec2 textureCoordinate;\n" +
            "\n" +
            "void main() {\n" +
            "    vec4 tc = texture2D(videoTex, textureCoordinate);\n" +
            //"    float color = tc.r * 0.3 + tc.g * 0.59 + tc.b * 0.11;\n" +  //所有视图修改成黑白
            //"    gl_FragColor = vec4(0.0, 0.0, 1, 1f);\n" +
            "    gl_FragColor = tc;\n" +
            "}\n";

    private FloatBuffer vertexBuffer;
    private FloatBuffer texVertexBuffer;
    private ShortBuffer vertexIndexBuffer;
    static final int COORDS_PER_VERTEX = 3;
    private final float[] VERTEX = {
        -1f, 1f, 0,  // top left
        -1f, -1f, 0, // bottom left
        1f, -1f, 0,  // bottom right
        1f, 1f, 0,   // top right
    };
    private final short[] VERTEX_INDEX = {
        0, 1, 2, 0, 2, 3
    };
    static final int COORDS_PER_TEX_VERTEX = 2;
    private int mvpMatrixHandler;
    private int stMatrixHandler;
    private int aPositionHandler;
    private int texCoordHandler;
    //private int videoTexHandler;

    private int mProgram;
    private int mOESTexName;

    TextureSquare() {
      mProgram = GLES20.glCreateProgram();
      int vertexShader = loadShader(GLES20.GL_VERTEX_SHADER, vertexShaderCode);
      int fragmentShader = loadShader(GLES20.GL_FRAGMENT_SHADER, fragmentShaderCode);
      GLES20.glAttachShader(mProgram, vertexShader);
      GLES20.glAttachShader(mProgram, fragmentShader);
      GLES20.glLinkProgram(mProgram);
      // 释放shader资源
      GLES20.glDeleteShader(vertexShader);
      GLES20.glDeleteShader(fragmentShader);
      vertexBuffer = toFloatBuffer(VERTEX);
      updateTexVertex(0, false, false);
      vertexIndexBuffer = toShortBuffer(VERTEX_INDEX);
      aPositionHandler = GLES20.glGetAttribLocation(mProgram, "aPosition");
      mvpMatrixHandler = GLES20.glGetUniformLocation(mProgram, "uMVPMatrix");
      stMatrixHandler = GLES20.glGetUniformLocation(mProgram, "uSTMatrix");
      texCoordHandler = GLES20.glGetAttribLocation(mProgram, "aTexCoord");
      mOESTexName = createOESTextureObject();
    }

    public void updateTexVertex(int rotation, boolean flipV, boolean flipH) {
      texVertexBuffer = toFloatBuffer(TextureRotationUtils.getRotation(rotation, flipV, flipH));
    }

    private int createOESTextureObject() {
      int[] tex = new int[1];
      //生成一个纹理
      GLES20.glGenTextures(1, tex, 0);
      //将此纹理绑定到外部纹理上
      GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, tex[0]);
      //设置纹理过滤参数
      GLES20.glTexParameterf(GLES11Ext.GL_TEXTURE_EXTERNAL_OES,
          GL10.GL_TEXTURE_MIN_FILTER, GL10.GL_NEAREST);
      GLES20.glTexParameterf(GLES11Ext.GL_TEXTURE_EXTERNAL_OES,
          GL10.GL_TEXTURE_MAG_FILTER, GL10.GL_LINEAR);
      GLES20.glTexParameterf(GLES11Ext.GL_TEXTURE_EXTERNAL_OES,
          GL10.GL_TEXTURE_WRAP_S, GL10.GL_CLAMP_TO_EDGE);
      GLES20.glTexParameterf(GLES11Ext.GL_TEXTURE_EXTERNAL_OES,
          GL10.GL_TEXTURE_WRAP_T, GL10.GL_CLAMP_TO_EDGE);
      //GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, 0);
      return tex[0];
    }

    public void draw(float[] vpMatrix, float[] stMatrix) {
      GLES20.glUseProgram(mProgram);
      GLES20.glEnableVertexAttribArray(aPositionHandler);
      GLES20.glVertexAttribPointer(aPositionHandler, COORDS_PER_VERTEX,
          GLES20.GL_FLOAT, false, 0, vertexBuffer);
      GLES20.glEnableVertexAttribArray(texCoordHandler);
      GLES20.glVertexAttribPointer(texCoordHandler, COORDS_PER_TEX_VERTEX,
          GLES20.GL_FLOAT, false, 0, texVertexBuffer);
      GLES20.glUniformMatrix4fv(mvpMatrixHandler, 1, false, vpMatrix, 0);
      GLES20.glUniformMatrix4fv(stMatrixHandler, 1, false, stMatrix, 0);
      GLES20.glDrawElements(GLES20.GL_TRIANGLES, VERTEX_INDEX.length, GLES20.GL_UNSIGNED_SHORT, vertexIndexBuffer);
    }

    public void destroy() {
      GLES20.glDeleteTextures(1, new int[] { mOESTexName }, 0);
      GLES20.glDeleteProgram(mProgram);
    }
  }

  public static class TextureRotationUtils {

    static final float TEXTURE[] = {
        0.0f, 1.0f,// 1 top left
        0.0f, 0.0f,// 2 bottom left
        1.0f, 0.0f,// 3 bottom right
        1.0f, 1.0f,// 4 top right
    };
    //逆时针
    static final float TEXTURE_90[] = {
        1.0f, 1.0f,// 4 top right
        0.0f, 1.0f,// 1 top left
        0.0f, 0.0f,// 2 bottom left
        1.0f, 0.0f,// 3 bottom right
    };
    static final float TEXTURE_180[] = {
        1.0f, 0.0f,// 3 bottom right
        1.0f, 1.0f,// 4 top right
        0.0f, 1.0f,// 1 top left
        0.0f, 0.0f,// 2 bottom left
    };
    static final float TEXTURE_270[] = {
        0.0f, 0.0f,// 2 bottom left
        1.0f, 0.0f,// 3 bottom right
        1.0f, 1.0f,// 4 top right
        0.0f, 1.0f,// 1 top left
    };
    static final float TEXTURE_OP[] = {
        0.0f, 1.0f,// 1 top left
        1.0f, 1.0f,// 4 top right
        1.0f, 0.0f,// 3 bottom right
        0.0f, 0.0f,// 2 bottom left
    };
    //逆时针
    static final float TEXTURE_OP_90[] = {
        1.0f, 1.0f,// 4 top right
        1.0f, 0.0f,// 3 bottom right
        0.0f, 0.0f,// 2 bottom left
        0.0f, 1.0f,// 1 top left
    };
    static final float TEXTURE_OP_180[] = {
        1.0f, 0.0f,// 3 bottom right
        0.0f, 0.0f,// 2 bottom left
        0.0f, 1.0f,// 1 top left
        1.0f, 1.0f,// 4 top right
    };
    static final float TEXTURE_OP_270[] = {
        0.0f, 0.0f,// 2 bottom left
        0.0f, 1.0f,// 1 top left
        1.0f, 1.0f,// 4 top right
        1.0f, 0.0f,// 3 bottom right
    };

    public static float[] getRotation(int rotation, boolean flipV, boolean flipH) {
      float[] rotatedTex;
      switch (rotation) {
        case 90:
          if (flipV && flipH) {
            rotatedTex = TEXTURE_270;
          } else if (flipV) {
            rotatedTex = TEXTURE_OP_180;
          } else if (flipH) {
            rotatedTex = TEXTURE_OP;
          } else {
            rotatedTex = TEXTURE_90;
          }
          break;
        case 180:
          if (flipV && flipH) {
            rotatedTex = TEXTURE;
          } else if (flipV) {
            rotatedTex = TEXTURE_OP_90;
          } else if (flipH) {
            rotatedTex = TEXTURE_OP_270;
          } else {
            rotatedTex = TEXTURE_180;
          }
          break;
        case 270:
          if (flipV && flipH) {
            rotatedTex = TEXTURE_90;
          } else if (flipV) {
            rotatedTex = TEXTURE_OP;
          } else if (flipH) {
            rotatedTex = TEXTURE_OP_180;
          } else {
            rotatedTex = TEXTURE_270;
          }
          break;
        default:
          if (flipV && flipH) {
            rotatedTex = TEXTURE_180;
          } else if (flipV) {
            rotatedTex = TEXTURE_OP_270;
          } else if (flipH) {
            rotatedTex = TEXTURE_OP_90;
          } else {
            rotatedTex = TEXTURE;
          }
          break;
      }
      return rotatedTex;
    }

  }

  static FloatBuffer toFloatBuffer(float[] arr) {
    FloatBuffer mBuffer;
    // 初始化ByteBuffer，长度为arr数组的长度*4
    ByteBuffer qbb = ByteBuffer.allocateDirect(arr.length * 4);
    // 数组排列用nativeOrder
    qbb.order(ByteOrder.nativeOrder());
    mBuffer = qbb.asFloatBuffer();
    mBuffer.put(arr);
    mBuffer.position(0);
    return mBuffer;
  }

  static ShortBuffer toShortBuffer(short[] arr) {
    ShortBuffer buffer;
    ByteBuffer bb = ByteBuffer.allocateDirect(arr.length * 2);
    bb.order(ByteOrder.nativeOrder());
    buffer = bb.asShortBuffer();
    buffer.put(arr);
    buffer.position(0);
    return buffer;
  }

  public static int loadShader(int type, String shaderCode){
    // 创造顶点着色器类型(GLES20.GL_VERTEX_SHADER)
    // 或者是片段着色器类型 (GLES20.GL_FRAGMENT_SHADER)
    int shader = GLES20.glCreateShader(type);
    // 添加上面编写的着色器代码并编译它
    GLES20.glShaderSource(shader, shaderCode);
    GLES20.glCompileShader(shader);
    return shader;
  }

  private static Camera.Size choosePreferredSize(List<Camera.Size> sizes, double aspectRatio) {
    List<Camera.Size> options = new ArrayList<>();
    for (Camera.Size option : sizes) {
      if(option.width == 1280 && option.height == 720)
        return option;
      if (Math.abs((int)(option.height * aspectRatio) - option.width) < 10) {
        options.add(option);
      }
    }
    if (options.size() > 0) {
      return Collections.max(options, new CompareSizesByArea());
    } else {
      return sizes.get(sizes.size() - 1);
    }
  }

  static class CompareSizesByArea implements Comparator<Size> {
    @Override
    public int compare(Camera.Size lhs, Camera.Size rhs) {
      // We cast here to ensure the multiplications won't overflow
      return Long.signum((long) lhs.width * lhs.height -
          (long) rhs.width * rhs.height);
    }
  }

  public static void logCameraSize(List<Camera.Size> list){
    if(list == null) {
      Log.e(TAG, "logCameraSizes: list is null");
      return;
    }
    for(Camera.Size size : list) {
      Log.d(TAG, "logCameraSizes: "+size.width+" x "+size.height);
    }
  }
}
